﻿using System;
using System.Linq;

namespace Alfa.Math {
	public class Vector : IEquatable<Vector> {
		private readonly int dimention;
		private float[] values;
		public Vector(int dimention) {
			this.dimention = dimention;
			this.values = new float[dimention];
		}
		public Vector(params float[] values)
			: this(values.Length) {
			this.values = values;
		}

		public int Dimention { get { return this.dimention; } }
		public float Length { get { return 0; } }
		public float[] Values { get { return this.values; } }

		public float this[int p] {
			get { if (values.Length > p) return values[p]; else throw new IndexOutOfRangeException( ); }
			set { if (values.Length > p) values[p] = value; else throw new IndexOutOfRangeException( ); }
		}

		public bool Equals(Vector other) {
			return this.dimention.Equals(other.dimention) && this.values.SequenceEqual(other.values);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Vector Add(Vector value1, Vector value2) {
			if (value1.Dimention != value2.Dimention)
				throw new Exception("Vectors do not have the same dimentions.");
			else {
				float[] vectorValues = new float[value1.Dimention];
				value1.Values.CopyTo(vectorValues, 0);
				for (int i = 0; i < value1.Dimention; i++) {
					vectorValues[i] += value2.Values[i];
				}
				return new Vector(vectorValues);
			}
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <param name="result">[OutAttribute] Sum of the source vectors.</param>
		public static void Add(ref Vector value1, ref Vector value2, out Vector result) {
			result = Add(value1, value2);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">The divisor.</param>
		/// <returns></returns>
		public static Vector Divide(Vector value1, float divisor) {
			float[] vectorValues = new float[value1.Dimention];
			value1.Values.CopyTo(vectorValues, 0);
			for (int i = 0; i < value1.Dimention; i++) {
				vectorValues[i] /= (float)divisor;
			}
			return new Vector(vectorValues);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">The divisor.</param>
		/// <param name="result">[OutAttribute] The result of the division.</param>
		public static void Divide(ref Vector value1, float divisor, out Vector result) {
			result = Divide(value1, divisor);
		}

		/// <summary>
		/// Returns a vector pointing in the opposite direction.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <returns></returns>
		public static Vector Negate(Vector value) {
			float[] vectorValues = new float[value.Dimention];
			for (int i = 0; i < value.Dimention; i++) {
				vectorValues[i] -= value.Values[i];
			}
			return new Vector(vectorValues);
		}

		/// <summary>
		/// Returns a vector pointing in the opposite direction.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="result">[OutAttribute] Vector pointing in the opposite direction.</param>
		public static void Negate(ref Vector value, out Vector result) {
			result = Negate(value);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <returns></returns>
		public static Vector Multiply(Vector value1, float scaleFactor) {
			float[] vectorValues = new float[value1.Dimention];
			value1.Values.CopyTo(vectorValues, 0);
			for (int i = 0; i < value1.Dimention; i++) {
				vectorValues[i] *= (float)scaleFactor;
			}
			return new Vector(vectorValues);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <param name="result">[OutAttribute] The result of the multiplication.</param>
		public static void Multiply(ref Vector value1, float scaleFactor, out Vector result) {
			result = Multiply(value1, scaleFactor);
		}

		/// <summary>
		/// Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Vector Subtract(Vector value1, Vector value2) {
			if (value1.Dimention != value2.Dimention)
				throw new Exception("Vectors do not have the same dimentions.");
			else {
				float[] vectorValues = new float[value1.Dimention];
				value1.Values.CopyTo(vectorValues, 0);
				for (int i = 0; i < value1.Dimention; i++) {
					vectorValues[i] += -value2.Values[i];
				}
				return new Vector(vectorValues);
			}
		}

		/// <summary>
		/// Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <param name="result">[OutAttribute] The result of the subtraction.</param>
		public static void Subtract(ref Vector value1, ref Vector value2, out Vector result) {
			result = Subtract(value1, value2);
		}

		/// <summary>
		/// Returns a vector pointing in the opposite direction.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <returns></returns>
		public static Vector operator -(Vector value) {
			return Negate(value);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Vector operator +(Vector value1, Vector value2) {
			return Add(value1, value2);
		}

		/// <summary>
		///  Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Vector operator -(Vector value1, Vector value2) {
			return Subtract(value1, value2);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <param name="value">Source vector.</param>
		/// <returns></returns>
		public static Vector operator *(float scaleFactor, Vector value) {
			return Multiply(value, scaleFactor);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <returns></returns>
		public static Vector operator *(Vector value, float scaleFactor) {
			return Multiply(value, scaleFactor);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="divider">The divisor.</param>
		/// <returns></returns>
		public static Vector operator /(Vector value, float divider) {
			return Divide(value, divider);
		}

		/// <summary>
		/// Tests vectors for equality.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static bool operator ==(Vector value1, Vector value2) {
			return value1.Equals(value2);
		}

		/// <summary>
		/// Tests vectors for inequality.
		/// </summary>
		/// <param name="value1">Vector to compare.</param>
		/// <param name="value2">Vector to compare.</param>
		/// <returns></returns>
		public static bool operator !=(Vector value1, Vector value2) {
			return !value1.Equals(value2);
		}

		public override bool Equals(object obj) {
			return obj.GetType( ).Equals(typeof(Vector)) && Equals((Vector)obj);
		}

		public override int GetHashCode( ) {
			return base.GetHashCode( );
		}

		//public override string ToString( ) {
		//	string vector = "[";
		//	foreach (var value in this.values) {
		//		vector += value.ToString( ) + ", ";
		//	}

		//	return vector.Substring(0, vector.Length - 2) + "]";
		//}
	}
}
